﻿using UnityEngine;
using System.Collections.Generic;

namespace Utility.DataStructure
{
	public class Dictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>>
	{
		public V this[K1 k1, K2 k2]
		{
			get
			{
				return this[k1][k2];
			}
			set
			{
				GetOrCreate (k1)[k2] = value;
			}
		}

		public bool ContainsKey (K1 k1, K2 k2)
		{
			if (ContainsKey (k1))
			{
				return this[k1].ContainsKey (k2);
			}
			return false;
		}

		public bool TryGetValue (K1 k1, K2 k2, out V val)
		{
			val = default (V);
			Dictionary<K2, V> dictK2;
			if (TryGetValue (k1, out dictK2))
			{
				return dictK2.TryGetValue (k2, out val);
			}
			return false;
		}

		public void Remove (K1 k1, K2 k2)
		{
			Dictionary<K2, V> dict;
			if (TryGetValue (k1, out dict))
			{
				dict.Remove (k2);
			}
		}

		public void Add (K1 k1, K2 k2, V val)
		{
			var dict = GetOrCreate (k1);
			dict.Add (k2, val);
		}

		protected Dictionary<K2, V> GetOrCreate (K1 k1)
		{
			Dictionary<K2, V> ret;
			if (!TryGetValue (k1, out ret))
			{
				ret = new Dictionary<K2, V> ();
				Add (k1, ret);
			}
			return ret;
		}
	}

	public class Dictionary<K1, K2, K3, V> : Dictionary<K1, K2, Dictionary<K3, V>>
	{
		public V this[K1 k1, K2 k2, K3 k3]
		{
			get
			{
				return this[k1][k2][k3];
			}
			set
			{
				GetOrCreate (k1, k2)[k3] = value;
			}
		}

		public bool ContainsKey (K1 k1, K2 k2, K3 k3)
		{
			if (ContainsKey (k1, k2))
			{
				return this[k1, k2].ContainsKey (k3);
			}
			return false;
		}

		public bool TryGetValue (K1 k1, K2 k2, K3 k3, out V val)
		{
			val = default (V);
			Dictionary<K3, V> dict;
			if (TryGetValue (k1, k2, out dict))
			{
				return dict.TryGetValue (k3, out val);
			}
			return false;
		}

		public void Remove (K1 k1, K2 k2, K3 k3)
		{
			Dictionary<K3, V> dict;
			if (TryGetValue (k1, k2, out dict))
			{
				dict.Remove (k3);
			}
		}

		public void Add (K1 k1, K2 k2, K3 k3, V val)
		{
			var dict = GetOrCreate (k1, k2);
			dict.Add (k3, val);
		}

		protected Dictionary<K3, V> GetOrCreate (K1 k1, K2 k2)
		{
			Dictionary<K3, V> ret;
			if (!TryGetValue (k1, k2, out ret))
			{
				ret = new Dictionary<K3, V> ();
				Add (k1, k2, ret);
			}
			return ret;
		}
	}

	public class Dictionary<K1, K2, K3, K4, V> : Dictionary<K1, K2, K3, Dictionary<K4, V>>
	{
		public V this[K1 k1, K2 k2, K3 k3, K4 k4]
		{
			get
			{
				return this[k1][k2][k3][k4];
			}
			set
			{
				GetOrCreate (k1, k2, k3)[k4] = value;
			}
		}

		public bool ContainsKey (K1 k1, K2 k2, K3 k3, K4 k4)
		{
			if (ContainsKey (k1, k2, k3))
			{
				return this[k1, k2, k3].ContainsKey (k4);
			}
			return false;
		}

		public bool TryGetValue (K1 k1, K2 k2, K3 k3, K4 k4, out V val)
		{
			val = default (V);
			Dictionary<K4, V> dict;
			if (TryGetValue (k1, k2, k3, out dict))
			{
				return dict.TryGetValue (k4, out val);
			}
			return false;
		}

		public void Remove (K1 k1, K2 k2, K3 k3, K4 k4)
		{
			Dictionary<K4, V> dict;
			if (TryGetValue (k1, k2, k3, out dict))
			{
				dict.Remove (k4);
			}
		}

		public void Add (K1 k1, K2 k2, K3 k3, K4 k4, V val)
		{
			var dict = GetOrCreate (k1, k2, k3);
			dict.Add (k4, val);
		}

		protected Dictionary<K4, V> GetOrCreate (K1 k1, K2 k2, K3 k3)
		{
			Dictionary<K4, V> ret;
			if (!TryGetValue (k1, k2, k3, out ret))
			{
				ret = new Dictionary<K4, V> ();
				Add (k1, k2, k3, ret);
			}
			return ret;
		}
	}
}